#include "npc.h"
#include "player.h"
#include "functions.h"
#include "globals.h"
#include "messages.h"
#include "BitStream.h"

std::map<int, Npc*> Npc::Npcs;

Npc::Npc(int id, std::string name, int x, int y)
{
    Npcs.insert(std::pair<int, Npc*>(id, this));

    this->id = id;
    this->name = name;

    pos.x = x;
    pos.y = y;
    velX = 0;
    velY = 0;
    movementSpeed = 256;

    combatPlayer = NULL;
}

Npc::~Npc()
{
    path.clear();
    Npcs.erase(this->id);
}

Npc* Npc::GetNpcFromID(int id)
{
    std::map<int, Npc*>::iterator i;
    i = Npcs.find(id);

    if (i != Npcs.end())
        return i->second;

    return NULL;
}

std::map<int, Npc*> Npc::GetNpcList()
{
    return Npcs;
}

void Npc::stop()
{
    path.clear();

    velX = 0;
    velY = 0;
}

/*void Npc::create_flag(int x, int y)
{
    flag.x = x;
    flag.y = y;

    inCombat = false;
    calculate_vel();
    send_flag();
}*/

void Npc::walk_to(int x, int y)
{
    std::vector<position> tempPath = find_path(pos.x, pos.y, x, y, true);

    if (not tempPath.empty())
    {
        path = tempPath;

        send_flag(path.back().x, path.back().y);

        if (combatPlayer != NULL)
        {
            combatPlayer = NULL;
            send_attack();
        }

        calculate_vel();
    }
}

void Npc::attack(Player* player)
{
    //if (player != NULL)
    std::vector<position> tempPath = find_path(pos.x, pos.y, player->x(), player->y(), false);

    path = tempPath;

    if (combatPlayer != player)
    {
        output.write("npc attacked someone!");
        combatPlayer = player;
        send_attack();
    }

    if (not path.empty())
    {
        send_flag(path.back().x, path.back().y);
        calculate_vel();
    }
}

void Npc::work(unsigned long deltaTicks)
{
    if (rand() % 2000 == 0)
    {
        int x, y;

        do
        {
            x = rand() % 40;
            y = rand() % 42;
        } while (tiles[y][x]->get_type() == TILE_STONEFLOOR);

        //create_flag(x * 64 + 32, y * 64 + 32);
        walk_to(tile_to_x(x) + rand() % 64 - 32, tile_to_y(y) + rand() % 64 - 32);
    }

    if (combatPlayer != NULL)
    {
        if (distance(pos.x, pos.y, combatPlayer->x(), combatPlayer->y()) <= 32)
        {
            //output.write("attack! :)");
        }
        else if (path.empty())
        {
            if (line_of_sight(x_to_tile(pos.x), y_to_tile(pos.y), x_to_tile(combatPlayer->x()), y_to_tile(combatPlayer->y())))
            {
                position att = attack_pos(combatPlayer);
                path.push_back(att);

                send_flag(path.back().x, path.back().y);
                calculate_vel();
            }
            else
            {
                attack(combatPlayer);
            }
        }
    }
}

void Npc::move(unsigned long deltaTicks)
{
    if (pos.x < 64)
    {
        //output->write("Travel west!");
        pos.x = 64;
    }

    if (pos.x + 64 > 2560)
    {
        //output->write("Travel east!");
        pos.x = 2560 - 64;
    }

    if (pos.y < 64)
    {
        //output->write("Travel north!");
        pos.y = 64;
    }

    if (pos.y + 64 > 2688)
    {
        //output->write("Travel south!");
        pos.y = 2688 - 64;
    }

    if (!path.empty())
    {
        if (pos.x != path.back().x)
        {
            pos.x += (deltaTicks / 1000.f) * velX;

            if (velX > 0)
            {
                if (pos.x > path.back().x)
                {
                    pos.x = path.back().x;
                }
            }
            else
            {
                if (pos.x < path.back().x)
                {
                    pos.x = path.back().x;
                }
            }
        }

        if (pos.y != path.back().y)
        {
            pos.y += (deltaTicks / 1000.f) * velY;

            if (velY > 0)
            {
                if (pos.y > path.back().y)
                {
                    pos.y = path.back().y;
                }
            }
            else
            {
                if (pos.y < path.back().y)
                {
                    pos.y = path.back().y;
                }
            }
        }

        if ((pos.x == path.back().x) and (pos.y == path.back().y))
        {
            //output.write("walked to pos");
            path.pop_back();

            if (path.empty())
            {
                stop();
            }
            else
            {
                send_flag(path.back().x, path.back().y);
                calculate_vel();
            }
        }
    }

    //output->write("POS: %f %f", pos.x, pos.y);
}

void Npc::calculate_vel()
{
    velX = movementSpeed * (path.back().x - pos.x) / distance(pos.x, pos.y, path.back().x, path.back().y);
    velY = movementSpeed * (path.back().y - pos.y) / distance(pos.x, pos.y, path.back().x, path.back().y);
}

void Npc::send_flag(int x, int y)
{
    RakNet::BitStream bsOut;

    bsOut.Write((RakNet::MessageID)ID_NPC_FLAG);

    bsOut.Write(id);
    bsOut.Write(x);
    bsOut.Write(y);

    net.broadcast_message(&bsOut, HIGH_PRIORITY, RELIABLE);
}

void Npc::send_attack()
{
    RakNet::BitStream bsOut;

    bsOut.Write((RakNet::MessageID)ID_NPC_ATTACK);

    if (combatPlayer != NULL)
        bsOut.Write(combatPlayer->get_guid());
    else
        bsOut.Write(0);

    net.broadcast_message(&bsOut, HIGH_PRIORITY, RELIABLE);
}

position Npc::attack_pos(Player* player)
{
    float x1, y1, x2, y2, xStep, yStep;

    x1 = pos.x;
    y1 = pos.y;
    x2 = 0;
    y2 = 0;

    if (player == NULL)
        return position(NULL, NULL);

    x2 = player->x();
    y2 = player->y();

    if (distance(x1, y1, x2, y2) <= 32)
    {
        return position(x1, y1);
    }

    xStep = (x2 - x1) / distance(x1, y1, x2, y2);
    yStep = (y2 - y1) / distance(x1, y1, x2, y2);

    //output.write("finding attack pos x1:%f y1:%f x2:%f y2:%f xStep:%f yStep:%f", x1, y1, x2, y2, xStep, yStep);

    do
    {
        x1 += xStep;
        y1 += yStep;
        //x2 -= xStep;
        //y2 -= yStep;
    }
    while (distance(x1, y1, x2, y2) > 32);

    return position(x1, y1);
}

bool Npc::moving()
{
    return !path.empty();
}

int Npc::get_id()
{
    return id;
}

std::string Npc::get_name()
{
    return name;
}

void Npc::set_x(int dest_x)
{
    pos.x = dest_x;
    //update_rect();
}

void Npc::set_y(int dest_y)
{
    pos.y = dest_y;
    //update_rect();
}

int Npc::x()
{
    return (int)pos.x;
}

int Npc::y()
{
    return (int)pos.y;
}

int Npc::flag_x()
{
    if (!path.empty())
    {
        return path.back().x;
    }
    else
    {
        return -1;
    }
}

int Npc::flag_y()
{
    if (!path.empty())
    {
        return path.back().y;
    }
    else
    {
        return -1;
    }
}
